package com.wyl.template.service.impl;

import com.wyl.template.constant.AppConstants;
import com.wyl.template.domain.common.ResponseResult;
import com.wyl.template.domain.dto.SysRoleDTO;
import com.wyl.template.domain.dto.UserRoleDTO;
import com.wyl.template.domain.model.SysUserDO;
import com.wyl.template.domain.model.SysUserRoleDO;
import com.wyl.template.exception.BusinessException;
import com.wyl.template.exception.ErrorCodeEnum;
import com.wyl.template.mapper.SysUserMapper;
import com.wyl.template.service.SysUserRoleService;
import com.wyl.template.service.SysUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wyl.template.util.CommonUtil;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * <p>
 * 用户表 服务实现类
 * </p>
 *
 * @author wyl
 * @since 2021-06-11
 */
@Service
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUserDO> implements SysUserService {

    @Autowired
    private SysUserMapper userMapper;

    @Autowired
    private SysUserRoleService userRoleService;

    /**
     * 根据用户名获得用户信息
     *
     * @param loginName
     * @return
     */
    @Override
    public SysUserDO getUserByLoginName(String loginName) {

        SysUserDO userDO = lambdaQuery().eq(SysUserDO::getLoginName, loginName).one();
        if (userDO == null) {
            throw new BusinessException(ErrorCodeEnum.GET_USER_INFO_ERR);
        }
        return userDO;
    }

    @Override
    public List<SysRoleDTO> listRoleByUser(Long userId) {
        return null;
    }

    /**
     * 新增用户
     *
     * @param userDTO
     * @return
     */
    @Transactional
    @Override
    public ResponseResult addUser(UserRoleDTO userDTO) {
        // 检查用户唯一性
        SysUserDO sysUserDO = lambdaQuery().eq(SysUserDO::getLoginName, userDTO.getLoginName()).one();
        if(sysUserDO != null){
            return ResponseResult.error(ErrorCodeEnum.USER_EXIST);
        }

        Subject subject = SecurityUtils.getSubject();
        SysUserDO currentUser = (SysUserDO) subject.getPrincipal();

        // 获得用户信息
        SysUserDO userDO = getSysUserDO(userDTO);

        // 设置默认密码
        String randomSalt = CommonUtil.getRandomSalt();
        ByteSource credentialsSalt = ByteSource.Util.bytes(randomSalt);
        Object pwd = new SimpleHash(AppConstants.DEFAULT_HASH_NAME, AppConstants.DEFAULT_USER_PWD, credentialsSalt, AppConstants.DEFAULT_HASH_TIMES);
        userDO.setPassword(pwd.toString());
        userDO.setSalt(randomSalt);
        userDO.setCreateBy(currentUser.getId());
        userDO.setCreateTime(new Date());

        // 保存用户
        save(userDO);
        // 获得当前用户信息，得到id赋值给userDTO，设置用户角色关系使用
        SysUserDO newUser = getUserByLoginName(userDTO.getLoginName());
        userDTO.setId(newUser.getId());
        // 更新用户角色关系
        updateUserRole(userDTO);

        return ResponseResult.success();
    }

    /**
     * 更新用户
     *
     * @param userDTO
     * @return
     */
    @Transactional
    @Override
    public ResponseResult updateUser(UserRoleDTO userDTO) {
        // 获得用户信息
        SysUserDO userDO = getSysUserDO(userDTO);
        // 只会更新有值的字段
        boolean res = updateById(userDO);
        if (!res) {
            throw new BusinessException(ErrorCodeEnum.UPDATE_FAILURE);
        }
        // 更新用户角色关系
        updateUserRole(userDTO);

        return ResponseResult.success();
    }

    /**
     * 拼装用户信息 - 用户新曾修改使用
     *
     * @param userDTO
     * @return
     */
    private SysUserDO getSysUserDO(UserRoleDTO userDTO) {
        Subject subject = SecurityUtils.getSubject();
        SysUserDO currentUser = (SysUserDO) subject.getPrincipal();

        SysUserDO userDO = new SysUserDO();
        BeanUtils.copyProperties(userDTO, userDO);
        userDO.setUpdateBy(currentUser.getId());
        userDO.setUpdateTime(new Date());

        return userDO;
    }

    /**
     * 更新用户角色关系
     *
     * @param userDTO
     */
    private void updateUserRole(UserRoleDTO userDTO) {
        List<SysUserRoleDO> userRoleList = new ArrayList<>();
        if (!ObjectUtils.isEmpty(userDTO.getRoles())) {
            userDTO.getRoles().forEach(item -> {
                SysUserRoleDO userRoleDO = new SysUserRoleDO();
                userRoleDO.setUserId(userDTO.getId());
                userRoleDO.setRoleId(item.getId());
                userRoleList.add(userRoleDO);
            });
        }
        //保存用户角色
        if (userRoleList.size() > 0) {
            userRoleService.updateUserRole(userDTO.getId(), userRoleList);
        }
    }

    /**
     * 删除用户
     *
     * @param userId
     * @return
     */
    @Transactional
    @Override
    public ResponseResult deleteUser(Long userId) {
        Subject subject = SecurityUtils.getSubject();
        SysUserDO currentUser = (SysUserDO) subject.getPrincipal();

//        userMapper.deleteById(userId);逻辑删除，不适用，要记录删除时间和删除人员

        // 逻辑删除
        lambdaUpdate().eq(SysUserDO::getId,userId)
                .set(SysUserDO::getUpdateBy,currentUser.getId()).set(SysUserDO::getUpdateTime,LocalDateTime.now()).set(SysUserDO::getDeleted,1)
                .update();

        // 删除用户角色关系
        userRoleService.updateUserRole(userId, null);

        return ResponseResult.success();
    }

    /**
     * 修改密码
     *
     * @param oldPassword
     * @param newPassword
     * @return
     */
    @Override
    public ResponseResult updatePassword(String oldPassword, String newPassword) {
        Subject subject = SecurityUtils.getSubject();
        SysUserDO userDO = (SysUserDO) subject.getPrincipal();
        // 验证原密码
        if (!validatePwd(userDO.getLoginName(), oldPassword)) {
            return ResponseResult.error(ErrorCodeEnum.USER_PWD_ERR);
        }
        //设置新密码
        ByteSource credentialsSalt = ByteSource.Util.bytes(userDO.getSalt());
        Object pwd = new SimpleHash(AppConstants.DEFAULT_HASH_NAME, newPassword, credentialsSalt, AppConstants.DEFAULT_HASH_TIMES);

        lambdaUpdate().eq(SysUserDO::getId, userDO.getId())
                .set(SysUserDO::getPassword, pwd.toString())
                .update();
        return ResponseResult.success();
    }

    /**
     * 重置密码
     *
     * @param userId
     * @return
     */
    @Override
    public ResponseResult resetPassword(Long userId) {
        Subject subject = SecurityUtils.getSubject();
        SysUserDO userDO = (SysUserDO) subject.getPrincipal();

//        String randomSalt = CommonUtil.getRandomCode(AppConstants.DEFAULT_SALT_LENGTH);
        String randomSalt = CommonUtil.getRandomSalt();
        ByteSource credentialsSalt = ByteSource.Util.bytes(randomSalt);
        Object pwd = new SimpleHash(AppConstants.DEFAULT_HASH_NAME, AppConstants.DEFAULT_USER_PWD, credentialsSalt, AppConstants.DEFAULT_HASH_TIMES);

        lambdaUpdate().eq(SysUserDO::getId, userId).set(SysUserDO::getPassword, pwd.toString()).set(SysUserDO::getSalt, randomSalt)
                .set(SysUserDO::getUpdateBy, userDO.getId()).set(SysUserDO::getUpdateTime, LocalDateTime.now())
                .update();

        return ResponseResult.success();
    }

    /**
     * 验证原始密码是否正确
     *
     * @param loginName
     * @param oldPassword
     * @return
     */
    private boolean validatePwd(String loginName, String oldPassword) {
        SysUserDO user = getUserByLoginName(loginName);
        String pwdDb = user.getPassword();

        ByteSource credentialsSalt = ByteSource.Util.bytes(user.getSalt());
        Object pwd = new SimpleHash(AppConstants.DEFAULT_HASH_NAME, oldPassword, credentialsSalt, AppConstants.DEFAULT_HASH_TIMES);

        return pwdDb.equals(pwd.toString());
    }
}
